Bellman-Ford算法求最短路

图论中比较基础的问题,求单源最短路,即在图中找一个点作为起点,求他到其他点的最短路,而Bellman-Ford算法是其中最简单的算法,相应地,其复杂度也比较高,效率也比较低,但是,他却可以判断图中是否存在负权回路(走一圈经过的权值是负数),因此可以处理带有负权边的图,且该算法是其他各种最短路算法的原始型,应当受到足够的重视。

设有图G< V, E >,点数为V,边数为E,源点为s点。我们用一个distance数组(下写为dis数组)来记录各点到s点的最短距离,将其初始化为INF(一般是个很大的常数),dis[s] = 0。

算法的基本思路是一种动态逼近的思想:
很显然,如果我们知道一条边起点到源点的最短距离和这条边的边权,那么终点到s点的最短距离为起点到源点的最短距离+边权,即 dis[终点] = dis[起点]+边权。
据此,我们执行V-1次对每一条边的松弛操作:对于该边,如果dis[起点]+边权 < dis[终点],则将dis[终点]变为dis[起点]+边权。

为什么是V-1次呢?

假如是第一次对所有边进行松弛操作,dis一开始被初始化为INF,所以那些s点直接相连的点,他们的dis会更新,他们与s点之间的边的松弛是有效的;
而其他的点,他们不与s点直接相连,那么对于所有与这些点有关的边,他们起点的初始dis值与终点的初始dis值均为INF,所以松弛条件不满足,无法松弛。

第二次时,所有与源点直接相连的点的dis值都是有效的,按第一次同样的原理,所有与这些点直接相连的点的dis值被有效更新,dis值有效的范围在经过两次对所有边的松弛之后,以源点为根树状地扩展了两层,所有与s点小于等于两条边相连的点的dis值被有效更新。

于是我们知道,在G图中,一个点最坏情况和s点之间有V-1条边,所以至多我们进行V-1此对所有边的松弛操作即可得出图中任何一点到s点的最短路。

如果有边,其边权为负值,那么其dis[起点]+边权 恒小于 dis[终点],则对其的松弛永远都能进行。所以我们可以再进行一次(即第V次)对所有边的松弛操作,边集中若无负边权,则各点dis值均为最短路,无法满足松弛条件,若存在负边权,则此边松弛条件依然是满足的,可据此判断出存在负边权。

伪代码过程:

声明图G <V, E>, 数组dis[], 源点s;
for(i = 1 to V) dis[i] = INF;
dis[s] = 0;
for(i = 1 to V-1)
    foreach 边∈G
        if(dis[起点]+边权 < dis[终点])
            dis[终点] = dis[起点] + 边权;
声明 flag = true; //若flag为false代表G中存在负边权
foreach 边∈G
    if(dis[起点]+边权 < dis[终点])
        flag = false;

最终dis[n]为n点到s点的最短路距离。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
import random import heapq # 生成无向图 def generate_graph(n, p): graph = [[0] * n for _ in range(n)] for i in range(n): for j in range(i+1, n): if random.random() < p: graph[i][j] = graph[j][i] = random.randint(1, 10) return graph # Prim算法最小生成树 def prim(graph): n = len(graph) visited = [False] * n heap = [(0, 0)] mst = [] while heap: weight, node = heapq.heappop(heap) if visited[node]: continue visited[node] = True mst.append((weight, node)) for i in range(n): if not visited[i] and graph[node][i] > 0: heapq.heappush(heap, (graph[node][i], i)) return mst # Kruskal算法最小生成树 def kruskal(graph): n = len(graph) edges = [] for i in range(n): for j in range(i+1, n): if graph[i][j] > 0: edges.append((graph[i][j], i, j)) edges.sort() parent = list(range(n)) mst = [] for weight, u, v in edges: pu, pv = find(parent, u), find(parent, v) if pu != pv: mst.append((weight, u, v)) parent[pu] = pv return mst def find(parent, x): if parent[x] != x: parent[x] = find(parent, parent[x]) return parent[x] # 生成图 graph = generate_graph(10, 0.6) print(graph) mst_prim = prim(graph) print("Prim算法最小生成树:", mst_prim) mst_kruskal = kruskal(graph) print("Kruskal算法最小生成树:", mst_kruskal) # Dijkstra算法最短路径 def dijkstra(graph, start, end): n = len(graph) dist = [float('inf')] * n dist[start] = 0 visited = [False] * n heap = [(0, start)] while heap: d, u = heapq.heappop(heap) if visited[u]: continue visited[u] = True for v in range(n): if graph[u][v] > 0: if dist[u] + graph[u][v] < dist[v]: dist[v] = dist[u] + graph[u][v] heapq.heappush(heap, (dist[v], v)) return dist[end] # Bellman-Ford算法最短路代码分析
最新发布
06-12

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值